package edu.hznu.retroSnakerPlanServer.runner;

import edu.hznu.retroSnakerPlanServer.controller.ProcessController;
import edu.hznu.retroSnakerPlanServer.dto.RequestMsg;
import edu.hznu.retroSnakerPlanServer.exception.UnknownMsgTypeException;
import edu.hznu.retroSnakerPlanServer.factory.ProcessControllerFactory;
import edu.hznu.retroSnakerPlanServer.service.ErrorProcess;
import edu.hznu.retroSnakerPlanServer.utils.ByteUtils;
import edu.hznu.retroSnakerPlanServer.utils.RequestBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * create by Cliven on 2018-06-03 10:59
 *
 * @author Cliven
 * 消息处理线程执行体
 */
public class MessageProcessRunner implements Runnable {
    private static final Logger log = LoggerFactory.getLogger(MessageProcessRunner.class);

    private DatagramPacket datagramPacket;
    private DatagramSocket datagramSocket;

    private MessageProcessRunner() {
    }

    public MessageProcessRunner(DatagramSocket datagramSocket, DatagramPacket datagramPacket) {
        this.datagramSocket = datagramSocket;
        if (datagramPacket == null) {
            throw new IllegalArgumentException("MessageProcessRunner datagramSocket cant no be null");
        }
        if (datagramPacket == null) {
            throw new IllegalArgumentException("MessageProcessRunner datagramPacket cant no be null");
        }
        this.datagramPacket = datagramPacket;
    }

    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see Thread#run()
     */
    @Override
    public void run() {
        ProcessController processController = ProcessControllerFactory.getInstance();
        byte[] respMsg = null;
        InetAddress ip = datagramPacket.getAddress();
        Integer port = datagramPacket.getPort();
        try {
            RequestMsg requestMsg = RequestBuilder.build(datagramPacket);
            // 根据类型处理消息
            Byte[] respMsgByte = processController.process(requestMsg);
            log.debug("response data length: {}", respMsgByte.length);
            log.debug("response data:{}", ByteUtils.bytesToHex(respMsgByte));


            // 如果响应的数据不为空，那么就直接响应
            if (respMsgByte != null) {
                respMsg = new byte[respMsgByte.length];
                for (int i = 0; i < respMsgByte.length; i++) {
                    respMsg[i] = respMsgByte[i];
                }
                // 响应数据到客户端请求发来的端口
                DatagramPacket responsePacket = new DatagramPacket(respMsg, respMsg.length, ip, port);
                datagramSocket.send(responsePacket);
            }
        } catch (UnknownMsgTypeException e) {
            log.info("Get a unknown message.");
        } catch (RuntimeException e) {
            log.error("Server exception when response msg:", e);
            respMsg = ErrorProcess.process(e);
            try {
                datagramSocket.send(new DatagramPacket(respMsg, respMsg.length, ip, port));
            } catch (IOException e1) {
                log.error("Server IO exception when response msg:", e);
            }
        } catch (IOException e) {
            log.error("Server IO exception when response msg:{}", e);
        }
    }
}
